#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
安全工具模块
提供统一的安全检查、输入验证、权限检查和敏感数据处理功能
"""

import os
import re
import subprocess
import hashlib
import secrets
import string
import tempfile
import stat
from typing import List, Dict, Any, Optional, Union
import logging

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class SecurityValidator:
    """安全验证器"""
    
    # 危险命令模式
    DANGEROUS_COMMANDS = [
        'rm -rf /', 'format', 'del /f /s /q', 'shutdown', 'reboot',
        'mkfs', 'fdisk', 'dd if=', 'chmod 777', 'chown root',
        'sudo su', 'su -', 'passwd', 'useradd', 'userdel'
    ]
    
    # 危险模式
    DANGEROUS_PATTERNS = [
        r';\s*rm\s+', r';\s*del\s+', r';\s*format\s+',
        r'\|\s*sh\s*$', r'\|\s*bash\s*$', r'\|\s*cmd\s*$',
        r'`[^`]*`', r'\$\([^)]*\)', r'>\s*/dev/[a-z]+',
        r'<\s*/dev/[a-z]+', r'2>&1.*\|.*sh',
        r':\(\)\{\s*:\|\:&\s*\}\s*;:', r'echo.*>.*\/etc\/'
    ]
    
    # 敏感数据模式
    SENSITIVE_PATTERNS = {
        'password': re.compile(r'password|passwd|pwd', re.IGNORECASE),
        'key': re.compile(r'key|secret|token|credential', re.IGNORECASE),
        'hash': re.compile(r'hash|md5|sha\d+', re.IGNORECASE),
        'private_key': re.compile(r'-----BEGIN.*PRIVATE KEY-----', re.MULTILINE),
        'api_key': re.compile(r'[A-Za-z0-9]{32,}', re.MULTILINE)
    }
    
    @staticmethod
    def validate_command(command: str) -> bool:
        """
        验证命令安全性
        
        Args:
            command: 要验证的命令
            
        Returns:
            bool: 命令是否安全
        """
        if not command or not isinstance(command, str):
            return False
            
        # 检查危险命令
        for dangerous_cmd in SecurityValidator.DANGEROUS_COMMANDS:
            if dangerous_cmd in command.lower():
                logger.warning(f"检测到危险命令: {dangerous_cmd}")
                return False
                
        # 检查危险模式
        for pattern in SecurityValidator.DANGEROUS_PATTERNS:
            if re.search(pattern, command):
                logger.warning(f"检测到危险模式: {pattern}")
                return False
                
        return True
    
    @staticmethod
    def validate_path(path: str) -> bool:
        """
        验证路径安全性
        
        Args:
            path: 要验证的路径
            
        Returns:
            bool: 路径是否安全
        """
        if not path or not isinstance(path, str):
            return False
            
        # 检查路径遍历攻击
        if '../' in path or '..\\'  in path:
            logger.warning(f"检测到路径遍历攻击: {path}")
            return False
            
        # 检查危险路径
        dangerous_paths = ['/etc/passwd', '/etc/shadow', '/boot/', '/sys/', '/proc/']
        for dangerous_path in dangerous_paths:
            if path.startswith(dangerous_path):
                logger.warning(f"访问危险路径: {path}")
                return False
                
        return True
    
    @staticmethod
    def validate_input(data: str, max_length: int = 1000) -> bool:
        """
        验证输入数据安全性
        
        Args:
            data: 输入数据
            max_length: 最大长度限制
            
        Returns:
            bool: 输入是否安全
        """
        if not isinstance(data, str):
            return False
            
        # 长度检查
        if len(data) > max_length:
            logger.warning(f"输入数据过长: {len(data)} > {max_length}")
            return False
            
        # 检查SQL注入模式
        sql_patterns = [
            r"'.*OR.*'", r'".*OR.*"', r'UNION.*SELECT',
            r'DROP.*TABLE', r'INSERT.*INTO', r'DELETE.*FROM'
        ]
        
        for pattern in sql_patterns:
            if re.search(pattern, data, re.IGNORECASE):
                logger.warning(f"检测到SQL注入模式: {pattern}")
                return False
        
        # 检查XSS模式
        xss_patterns = [
            r'<script.*?>.*?</script>', r'<.*?on\w+\s*=.*?>',
            r'javascript:', r'vbscript:', r'<iframe.*?>',
            r'<object.*?>', r'<embed.*?>', r'<link.*?>'
        ]
        
        for pattern in xss_patterns:
            if re.search(pattern, data, re.IGNORECASE):
                logger.warning(f"检测到XSS模式: {pattern}")
                return False
        
        # 检查命令注入模式
        cmd_patterns = [
            r';\s*\w+', r'\|\s*\w+', r'`.*?`', r'\$\(.*?\)',
            r'&&\s*\w+', r'\|\|\s*\w+'
        ]
        
        for pattern in cmd_patterns:
            if re.search(pattern, data):
                logger.warning(f"检测到命令注入模式: {pattern}")
                return False
                
        return True
    
    @staticmethod
    def sanitize_filename(filename: str) -> str:
        """
        清理文件名，移除危险字符
        
        Args:
            filename: 原始文件名
            
        Returns:
            str: 清理后的文件名
        """
        # 移除危险字符
        safe_filename = re.sub(r'[<>:"/\\|?*]', '_', filename)
        
        # 移除控制字符
        safe_filename = ''.join(char for char in safe_filename if ord(char) >= 32)
        
        # 限制长度
        if len(safe_filename) > 255:
            safe_filename = safe_filename[:255]
            
        return safe_filename

class PermissionChecker:
    """权限检查器"""
    
    @staticmethod
    def check_file_permissions(file_path: str) -> Dict[str, Any]:
        """
        检查文件权限
        
        Args:
            file_path: 文件路径
            
        Returns:
            dict: 权限信息
        """
        try:
            if not os.path.exists(file_path):
                return {'error': '文件不存在'}
                
            file_stat = os.stat(file_path)
            mode = file_stat.st_mode
            
            permissions = {
                'owner_read': bool(mode & stat.S_IRUSR),
                'owner_write': bool(mode & stat.S_IWUSR),
                'owner_execute': bool(mode & stat.S_IXUSR),
                'group_read': bool(mode & stat.S_IRGRP),
                'group_write': bool(mode & stat.S_IWGRP),
                'group_execute': bool(mode & stat.S_IXGRP),
                'other_read': bool(mode & stat.S_IROTH),
                'other_write': bool(mode & stat.S_IWOTH),
                'other_execute': bool(mode & stat.S_IXOTH),
                'octal': oct(mode)[-3:],
                'uid': file_stat.st_uid,
                'gid': file_stat.st_gid
            }
            
            # 检查危险权限
            warnings = []
            if permissions['other_write']:
                warnings.append('文件对其他用户可写')
            if permissions['other_execute'] and not os.path.isdir(file_path):
                warnings.append('文件对其他用户可执行')
            if permissions['octal'] in ['777', '666']:
                warnings.append('文件权限过于宽松')
                
            permissions['warnings'] = warnings
            return permissions
            
        except Exception as e:
            return {'error': f'检查权限失败: {e}'}
    
    @staticmethod
    def check_root_required() -> bool:
        """
        检查是否需要root权限
        
        Returns:
            bool: 是否为root用户
        """
        try:
            # 在Windows上检查管理员权限
            if os.name == 'nt':
                import ctypes
                return ctypes.windll.shell32.IsUserAnAdmin() != 0
            else:
                # 在Unix/Linux上检查root权限
                return os.geteuid() == 0
        except Exception:
            return False
    
    @staticmethod
    def secure_file_create(file_path: str, content: str, mode: int = 0o600) -> bool:
        """
        安全创建文件
        
        Args:
            file_path: 文件路径
            content: 文件内容
            mode: 文件权限模式
            
        Returns:
            bool: 是否创建成功
        """
        try:
            # 验证路径安全性
            if not SecurityValidator.validate_path(file_path):
                return False
                
            # 创建临时文件
            temp_fd, temp_path = tempfile.mkstemp()
            try:
                with os.fdopen(temp_fd, 'w') as temp_file:
                    temp_file.write(content)
                
                # 设置权限
                os.chmod(temp_path, mode)
                
                # 原子性移动
                os.rename(temp_path, file_path)
                return True
                
            except Exception:
                # 清理临时文件
                if os.path.exists(temp_path):
                    os.unlink(temp_path)
                raise
                
        except Exception as e:
            logger.error(f"安全创建文件失败: {e}")
            return False

class SensitiveDataHandler:
    """敏感数据处理器"""
    
    @staticmethod
    def generate_secure_password(length: int = 16) -> str:
        """
        生成安全密码
        
        Args:
            length: 密码长度
            
        Returns:
            str: 生成的密码
        """
        if length < 8:
            length = 8
            
        # 确保包含各种字符类型
        chars = string.ascii_letters + string.digits + "!@#$%^&*"
        password = ''.join(secrets.choice(chars) for _ in range(length))
        
        # 验证密码强度
        if (any(c.islower() for c in password) and
            any(c.isupper() for c in password) and
            any(c.isdigit() for c in password) and
            any(c in "!@#$%^&*" for c in password)):
            return password
        else:
            # 递归重新生成
            return SensitiveDataHandler.generate_secure_password(length)
    
    @staticmethod
    def hash_password(password: str, salt: Optional[str] = None) -> Dict[str, str]:
        """
        安全哈希密码
        
        Args:
            password: 原始密码
            salt: 盐值（可选）
            
        Returns:
            dict: 包含哈希值和盐值的字典
        """
        if salt is None:
            salt = secrets.token_hex(16)
            
        # 使用PBKDF2进行哈希
        password_hash = hashlib.pbkdf2_hmac(
            'sha256',
            password.encode('utf-8'),
            salt.encode('utf-8'),
            100000  # 迭代次数
        )
        
        return {
            'hash': password_hash.hex(),
            'salt': salt,
            'algorithm': 'pbkdf2_sha256',
            'iterations': 100000
        }
    
    @staticmethod
    def mask_sensitive_data(data: str) -> str:
        """
        掩码敏感数据
        
        Args:
            data: 原始数据
            
        Returns:
            str: 掩码后的数据
        """
        masked_data = data
        
        # 掩码密码
        masked_data = re.sub(
            r'(password|passwd|pwd)\s*[:=]\s*[^\s]+',
            r'\1: ****',
            masked_data,
            flags=re.IGNORECASE
        )
        
        # 掩码密钥
        masked_data = re.sub(
            r'(key|secret|token)\s*[:=]\s*[^\s]+',
            r'\1: ****',
            masked_data,
            flags=re.IGNORECASE
        )
        
        # 掩码长字符串（可能是密钥）
        masked_data = re.sub(
            r'\b[A-Za-z0-9]{32,}\b',
            '****',
            masked_data
        )
        
        return masked_data
    
    @staticmethod
    def detect_sensitive_data(content: str) -> List[Dict[str, Any]]:
        """
        检测敏感数据
        
        Args:
            content: 要检测的内容
            
        Returns:
            list: 检测到的敏感数据列表
        """
        detections = []
        
        for data_type, pattern in SecurityValidator.SENSITIVE_PATTERNS.items():
            matches = pattern.finditer(content)
            for match in matches:
                detections.append({
                    'type': data_type,
                    'content': match.group(),
                    'start': match.start(),
                    'end': match.end(),
                    'line': content[:match.start()].count('\n') + 1
                })
                
        return detections

class SecureCommandExecutor:
    """安全命令执行器"""
    
    @staticmethod
    def execute_safe_command(command: str, timeout: int = 30, 
                           allowed_commands: Optional[List[str]] = None) -> Dict[str, Any]:
        """
        安全执行命令
        
        Args:
            command: 要执行的命令
            timeout: 超时时间
            allowed_commands: 允许的命令列表
            
        Returns:
            dict: 执行结果
        """
        try:
            # 验证命令安全性
            if not SecurityValidator.validate_command(command):
                return {'error': '命令不安全', 'returncode': -1}
            
            # 检查命令白名单
            if allowed_commands:
                cmd_name = command.split()[0]
                if cmd_name not in allowed_commands:
                    return {'error': f'命令不在白名单中: {cmd_name}', 'returncode': -1}
            
            # 执行命令
            result = subprocess.run(
                command,
                shell=True,
                capture_output=True,
                text=True,
                timeout=timeout
            )
            
            return {
                'stdout': result.stdout,
                'stderr': result.stderr,
                'returncode': result.returncode,
                'command': command
            }
            
        except subprocess.TimeoutExpired:
            return {'error': '命令执行超时', 'returncode': -1}
        except Exception as e:
            return {'error': f'命令执行失败: {e}', 'returncode': -1}

# 全局安全检查函数
def security_audit(directory: str) -> Dict[str, Any]:
    """
    对目录进行安全审计
    
    Args:
        directory: 要审计的目录
        
    Returns:
        dict: 审计结果
    """
    audit_results = {
        'directory': directory,
        'file_permissions': [],
        'sensitive_data': [],
        'security_issues': []
    }
    
    try:
        for root, dirs, files in os.walk(directory):
            for file in files:
                file_path = os.path.join(root, file)
                
                # 检查文件权限
                perms = PermissionChecker.check_file_permissions(file_path)
                if perms.get('warnings'):
                    audit_results['file_permissions'].append({
                        'file': file_path,
                        'warnings': perms['warnings']
                    })
                
                # 检查敏感数据（仅文本文件）
                if file.endswith(('.py', '.txt', '.conf', '.cfg', '.ini', '.yaml', '.yml')):
                    try:
                        with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                            content = f.read()
                            sensitive_data = SensitiveDataHandler.detect_sensitive_data(content)
                            if sensitive_data:
                                audit_results['sensitive_data'].append({
                                    'file': file_path,
                                    'detections': sensitive_data
                                })
                    except Exception:
                        pass  # 忽略无法读取的文件
                        
    except Exception as e:
        audit_results['security_issues'].append(f'审计过程中出错: {e}')
    
    return audit_results

# 导出主要类和函数
__all__ = [
    'SecurityValidator',
    'PermissionChecker', 
    'SensitiveDataHandler',
    'SecureCommandExecutor',
    'security_audit'
]